package com.party.web.biz.quartz.impl;

import com.alibaba.druid.support.json.JSONUtils;
import com.party.common.constant.Constant;
import com.party.common.utils.StringUtils;
import com.party.core.exception.ScheduleException;
import com.party.core.model.notify.Event;
import com.party.core.model.quartz.QuartzDataMap;
import com.party.core.service.quartz.IQuartzDataMapService;
import com.party.web.biz.quartz.ISchsduleService;
import com.party.web.biz.quartz.factory.AsyncJobFactory;
import com.party.web.biz.quartz.factory.SyncJobFactory;
import org.apache.commons.collections.CollectionUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 定时任务实现
 * Created by wei.li
 *
 * @date 2017/4/12 0012
 * @time 10:57
 */
@Service
public class SchsduleService implements ISchsduleService {

    private static String JOB_GROUP = "jobGroup";

    public static final String GROUP_SEND_SHORT_MESSAGE = "sendShortMessageGroup_web";

    public static final String JOB_SEND_SHORT_MESSAGE = "定时发送短信";

    public static final String PARAMS = "params";

    public static final String IS_BIZ = "isBiz";

    public static final String SEND_SHORT_MESSAGE_LINE = "_";

    private static final Logger logger = LoggerFactory.getLogger(SchsduleService.class);

    @Autowired
    private IQuartzDataMapService quartzDataMapService;


    /**
     * 获取触发器key
     * @param jobName 任务名称
     * @return 目标key
     */
    @Override
    public TriggerKey getTriggerKey(String jobName) {
        return TriggerKey.triggerKey(jobName, JOB_GROUP);
    }

    /**
     * 获取表达式触发器
     *
     * @param scheduler 调度者
     * @param jobName 任务名称
     * @return 任务
     */
    @Override
    public CronTrigger getCronTrigger(Scheduler scheduler, String jobName) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, JOB_GROUP);
            return (CronTrigger) scheduler.getTrigger(triggerKey);
        } catch (SchedulerException e) {
            logger.error("获取定时任务CronTrigger出现异常", e);
            throw new ScheduleException("获取定时任务CronTrigger出现异常");
        }
    }

    /**
     * 移除触发器
     *
     * @param scheduler 调度者
     * @param triggerName 任务名称
     * @return 任务
     */
    @Override
    public void removeTrigger(Scheduler scheduler, String triggerName, String group) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, group);
            if (null == scheduler.getTrigger(triggerKey)) {
                return;
            }
            scheduler.pauseTrigger(triggerKey);
            scheduler.unscheduleJob(triggerKey);
        } catch (SchedulerException e) {
            logger.error("获取定时任务CronTrigger出现异常", e);
            throw new ScheduleException("获取定时任务CronTrigger出现异常");
        }
    }

    /**
     * 获取表达式触发器
     *
     * @param scheduler 调度者
     * @param triggerName 任务名称
     * @return 任务
     */
    @Override
    public CronTrigger getCronTrigger(Scheduler scheduler, String triggerName, String group) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, group);
            return (CronTrigger) scheduler.getTrigger(triggerKey);
        } catch (SchedulerException e) {
            logger.error("获取定时任务CronTrigger出现异常", e);
            throw new ScheduleException("获取定时任务CronTrigger出现异常");
        }
    }

    /**
     * 创建任务
     *
     * @param scheduler 调度者
     * @param event 事件编号
     */
    @Override
    public void createScheduleJob(Scheduler scheduler, Event event, String bizId, String group, List<Object> params) {
        boolean isSync = event.getIsSync().equals(0);
        createScheduleJob(scheduler, event.getName(), event.getCronExpression(), isSync, event, bizId, group, params);
    }

    /**
     * 创建任务
     *
     * @param scheduler 调度者
     * @param jobName 任务名称
     * @param cronExpression 任务
     * @param isSync 是否异步
     */
    @Override
    public void createScheduleJob(Scheduler scheduler, String jobName, String cronExpression, boolean isSync, Event event,
                                  String bizId, String group, List<Object> params) {
        // 业务主键为空, 走以前的系统添加定时任务模式
        if (StringUtils.isEmpty(bizId)) {
            //同步或异步
            Class<? extends Job> jobClass = isSync ? AsyncJobFactory.class : SyncJobFactory.class;

            //构建job信息
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, JOB_GROUP).build();

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, JOB_GROUP).withSchedule(scheduleBuilder).build();

            //放入参数，运行时的方法可以获取
            jobDetail.getJobDataMap().put(Constant.JOB_PARAM_KEY, event);
            jobDetail.getJobDataMap().put(IS_BIZ, false);
            try {
                scheduler.scheduleJob(jobDetail, trigger);
            } catch (SchedulerException e) {
                logger.error("创建定时任务失败", e);
                throw new ScheduleException("创建定时任务失败");
            }
        } else {
            // 业务主键不为空, 为程序动态添加的定时任务
            if (StringUtils.isEmpty(group)) {
                throw new ScheduleException("job组名不能为空");
            }
            //同步或异步
            Class<? extends Job> jobClass = isSync ? AsyncJobFactory.class : SyncJobFactory.class;

            // 获取或者创建jobDetail
            JobKey jobKey = JobKey.jobKey(jobName, group);
            boolean hasJob = true;
            //构建job信息
            JobDetail jobDetail = null;
            try {
                jobDetail = scheduler.getJobDetail(jobKey);
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
            if (null == jobDetail) {
                jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, group).build();
                hasJob = false;
            }

            // 储存参数信息
            if (CollectionUtils.isNotEmpty(params)) {
                String className = List.class.getName();
                String json = JSONUtils.toJSONString(params);
                String targetId = PARAMS.concat(SEND_SHORT_MESSAGE_LINE).concat(bizId);
                QuartzDataMap dbMap = quartzDataMapService.findByTargetId(targetId);
                if (null != dbMap) {
                    dbMap.setClassName(className);
                    dbMap.setContent(json);
                    quartzDataMapService.update(dbMap);
                } else {
                    QuartzDataMap quartzDataMap = new QuartzDataMap(targetId, className, json);
                    quartzDataMapService.insert(quartzDataMap);
                }
            }
            JobDataMap dataMap = jobDetail.getJobDataMap();
//            dataMap.put(PARAMS.concat(SEND_SHORT_MESSAGE_LINE).concat(bizId), params);
            if (null == dataMap.get(Constant.JOB_PARAM_KEY)) {
                dataMap.put(Constant.JOB_PARAM_KEY, event);
            }
            if (null == dataMap.get(IS_BIZ)) {
                dataMap.put(IS_BIZ, true);
            }

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            try {
                if (hasJob) {
                    // 如果job本来已经存在了
                    CronTrigger trigger = TriggerBuilder.newTrigger()
                            .withSchedule(scheduleBuilder)
                            .withIdentity(jobName.concat(SEND_SHORT_MESSAGE_LINE).concat(bizId), group)
                            .forJob(jobDetail)
                            .build();
                    scheduler.scheduleJob(trigger);
                } else {
                    CronTrigger trigger = TriggerBuilder.newTrigger()
                            .withSchedule(scheduleBuilder)
                            .withIdentity(jobName.concat(SEND_SHORT_MESSAGE_LINE).concat(bizId), group)
                            .build();
                    scheduler.scheduleJob(jobDetail, trigger);
                }
            } catch (SchedulerException e) {
                logger.error("创建定时任务失败", e);
                throw new ScheduleException("创建定时任务失败");
            }
        }
    }

    /**
     * 运行一次任务
     *
     * @param scheduler 调度者
     * @param jobName 任务名称
     */
    @Override
    public void runOnce(Scheduler scheduler, String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP);
        try {
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            logger.error("运行一次定时任务失败", e);
            throw new ScheduleException("运行一次定时任务失败");
        }
    }

    /**
     * 暂停任务
     *
     * @param scheduler 调度者
     * @param jobName 任务名称
     */
    @Override
    public void pauseJob(Scheduler scheduler, String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP);
        try {
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            logger.error("暂停定时任务失败", e);
            throw new ScheduleException("暂停定时任务失败");
        }
    }

    /**
     * 恢复任务
     *
     * @param scheduler 调度者
     * @param jobName 任务名称
     */
    @Override
    public void resumeJob(Scheduler scheduler, String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP);
        try {
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            logger.error("暂停定时任务失败", e);
            throw new ScheduleException("暂停定时任务失败");
        }
    }

    /**
     * 获取jobKey
     *
     * @param jobName 任务名称
     * @return jobKey
     */
    @Override
    public JobKey getJobKey(String jobName) {
        return JobKey.jobKey(jobName, JOB_GROUP);
    }

    /**
     * 更新定时任务
     *
     * @param scheduler 调度者
     * @param event 事件
     */
    @Override
    public void updateScheduleJob(Scheduler scheduler, Event event, String bizId, String group) {
        updateScheduleJob(scheduler, event.getName(), event.getCronExpression(), bizId, group);
    }

    /**
     * 更新定时任务
     *
     * @param scheduler 调度者
     * @param jobName 任务名称
     * @param cronExpression 任务表达式
     */
    @Override
    public void updateScheduleJob(Scheduler scheduler, String jobName, String cronExpression, String bizId, String group) {
        try {
            TriggerKey triggerKey;
            if (StringUtils.isEmpty(bizId)) {
                triggerKey = this.getTriggerKey(jobName);
            } else {
                triggerKey = TriggerKey.triggerKey(jobName.concat(SEND_SHORT_MESSAGE_LINE).concat(bizId), group);
            }

            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            //按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());

            // 忽略状态为PAUSED的任务，解决集群环境中在其他机器设置定时任务为PAUSED状态后，集群环境启动另一台主机时定时任务全被唤醒的bug
            if(!triggerState.name().equalsIgnoreCase("PAUSED")){

                //按新的trigger重新设置job执行
                scheduler.rescheduleJob(triggerKey, trigger);
            }
        } catch (SchedulerException e) {
            logger.error("更新定时任务失败", e);
            throw new ScheduleException("更新定时任务失败");
        }
    }

    /**
     * 删除定时任务
     *
     * @param scheduler 调度者
     * @param jobName 任务名称
     */
    @Override
    public void deleteScheduleJob(Scheduler scheduler, String jobName) {
        try {
            scheduler.deleteJob(getJobKey(jobName));
        } catch (SchedulerException e) {
            logger.error("删除定时任务失败", e);
            throw new ScheduleException("删除定时任务失败");
        }
    }
}
